#include "BLPatch.h"
#include "Ray.h"
#include "Console.h"
#include "Lambert.h"
#include <iostream>

using namespace std;

BLPatch::BLPatch()
{
}

BLPatch::~BLPatch()
{
}

void
BLPatch::renderGL()
{
	Vector3 mid_point = Vector3(0.0f);
	for(int i=0; i<4; i++)
		mid_point += m_verts[i];
	mid_point /= 4;
	
	glBegin(GL_TRIANGLE_FAN);	
		glVertex3f(mid_point.x, mid_point.y, mid_point.z);
		glVertex3f(m_verts[0].x, m_verts[0].y, m_verts[0].z);
		glVertex3f(m_verts[1].x, m_verts[1].y, m_verts[1].z);
		glVertex3f(m_verts[2].x, m_verts[2].y, m_verts[2].z);
		glVertex3f(m_verts[3].x, m_verts[3].y, m_verts[3].z);
		glVertex3f(m_verts[0].x, m_verts[0].y, m_verts[0].z);
	glEnd();
}

float getT(Vector3 o, Vector3 d, Vector3 p)
{
    float t;
    if(fabs(d[0])>=fabs(d[1]) && fabs(d[0])>=fabs(d[2]))
        t = (p[0]-o[0])/d[0];
    else if(fabs(d[1])>=fabs(d[2])&&fabs(d[1])>=fabs(d[0]))
        t = (p[1]-o[1])/d[1];
    else
        t = (p[2]-o[2])/d[2];
    return t;
}


float
BLPatch::getU(float v)
{
    float aa = v*A[1]+B[1],
          bb = v*(A[1]-A[0])+B[1]-B[0];
    if(fabs(bb)>=fabs(aa))
        return (v*(C[0]-C[1])+D[0]-D[1])/bb;
    else
        return (-v*C[1]-D[1])/aa;
}

Vector3 getNormal(float u, float v, Vector3 U1, Vector3 U2, Vector3 V1, Vector3 V2)
{
	Vector3 tanV = (1-v)*V1 + v*V2,
			tanU = (1-u)*U1 + u*U2;
			
	return cross(tanU,tanV).normalized();
}

bool
BLPatch::solve(const Ray &ray, HitInfo &hit, float v, float tMin, float tMax)
{
    if(v>=0 && v<=1)
    {
        float u = getU(v);
        if(u>=0&&u<=1)
        {
           // if(u<0.1 || u>0.9 || v<0.1 || v>0.9)
            {
                hit.P = u*v*a + u*b + v*c + d;
                hit.N = getNormal(u,v,m_verts[3]-m_verts[0],m_verts[2]-m_verts[1],m_verts[1]-m_verts[0],m_verts[2]-m_verts[3]);
                hit.t = getT(ray.o, ray.d, hit.P);
                hit.material = (Material *) new Lambert(Vector3(u,v,0));
                if(hit.t>tMin && hit.t<tMax)
                    return true;
            }
        }
    }
    return false;
}

bool
BLPatch::intersect(HitInfo& result, const Ray& ray,
                   float tMin, float tMax)
{
    a = m_verts[0] + m_verts[2] - m_verts[1] - m_verts[3];
    b = m_verts[3] - m_verts[0];
    c = m_verts[1] - m_verts[0];
    d = m_verts[0];

	A[0] = a[0]*ray.d[2] - a[2]*ray.d[0];
	A[1] = a[1]*ray.d[2] - a[2]*ray.d[1];
	B[0] = b[0]*ray.d[2] - b[2]*ray.d[0];
	B[1] = b[1]*ray.d[2] - b[2]*ray.d[1];
	C[0] = c[0]*ray.d[2] - c[2]*ray.d[0];
	C[1] = c[1]*ray.d[2] - c[2]*ray.d[1];
	D[0] = (d[0]-ray.o[0])*ray.d[2] - (d[2]-ray.o[2])*ray.d[0];
	D[1] = (d[1]-ray.o[1])*ray.d[2] - (d[2]-ray.o[2])*ray.d[1];
	
	float x = A[1]*C[0]-A[0]*C[1],
          y = A[1]*D[0]-A[0]*D[1]+B[1]*C[0]-B[0]*C[1],
          z = B[1]*D[0]-B[0]*D[1];
		  
	float root = y*y-4*x*z;
	if(root <0)
		return false;

	if(root == 0)
	{
		//cout << "Got here" << endl;
		float u,v,t;
		v = -y/(2*x);
		return solve(ray,result,v,tMin,tMax);
	}
	else
	{
		float v[2];
		v[0] = (-y+sqrt(root))/(2*x),
		v[1] = (-y-sqrt(root))/(2*x);
		bool res[2];
		HitInfo tmp1, tmp2;
		res[0] = solve(ray,tmp1,v[0],tMin,tMax);
		res[1] = solve(ray,tmp2,v[1],tMin,tMax);
		if(res[0]==false && res[1] == true)
		{
			result.P = tmp2.P;
			result.N = tmp2.N;
			result.t = tmp2.t;
			result.material = tmp2.material;//m_material;
		    return res[1];
		}
		else if(res[1]==false && res[0] == true)
		{
			result.P = tmp1.P;
			result.N = tmp1.N;
			result.t = tmp1.t;
			result.material = tmp1.material;//m_material;
		    return res[0];
		}
		else
		{
			if(res[0]==true && res[1]==true)
			if(tmp1.t<tmp2.t)
			{
				result.P = tmp1.P;//Vector3(0,0,0);
		    	result.N = tmp1.N;//Vector3(0,1,0);
		    	result.t = tmp1.t;//1;
		    	result.material = tmp1.material;//m_material;
		        return res[0];
			}
			else
			{
				result.P = tmp2.P;//Vector3(0,0,0);
		    	result.N = tmp2.N;//Vector3(0,1,0);
		    	result.t = tmp2.t;//1;
		    	result.material = tmp2.material;//m_material;
		        return res[1];
			}
		}
	}
	
    return false;
}
